/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var echarts = require("../../echarts");

var zrUtil = require("static/plugins/js/zrender/lib/core/util");

var graphic = require("../../util/graphic");

var _symbol = require("../../util/symbol");

var createSymbol = _symbol.createSymbol;

var _number = require("../../util/number");

var parsePercent = _number.parsePercent;
var isNumeric = _number.isNumeric;

var _helper = require("./helper");

var setLabel = _helper.setLabel;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var BAR_BORDER_WIDTH_QUERY = ["itemStyle", "borderWidth"]; // index: +isHorizontal

var LAYOUT_ATTRS = [
    {
        xy: "x",
        wh: "width",
        index: 0,
        posDesc: ["left", "right"],
    },
    {
        xy: "y",
        wh: "height",
        index: 1,
        posDesc: ["top", "bottom"],
    },
];
var pathForLineWidth = new graphic.Circle();
var BarView = echarts.extendChartView({
    type: "pictorialBar",
    render: function (seriesModel, ecModel, api) {
        var group = this.group;
        var data = seriesModel.getData();
        var oldData = this._data;
        var cartesian = seriesModel.coordinateSystem;
        var baseAxis = cartesian.getBaseAxis();
        var isHorizontal = !!baseAxis.isHorizontal();
        var coordSysRect = cartesian.grid.getRect();
        var opt = {
            ecSize: {
                width: api.getWidth(),
                height: api.getHeight(),
            },
            seriesModel: seriesModel,
            coordSys: cartesian,
            coordSysExtent: [
                [coordSysRect.x, coordSysRect.x + coordSysRect.width],
                [coordSysRect.y, coordSysRect.y + coordSysRect.height],
            ],
            isHorizontal: isHorizontal,
            valueDim: LAYOUT_ATTRS[+isHorizontal],
            categoryDim: LAYOUT_ATTRS[1 - isHorizontal],
        };
        data.diff(oldData)
            .add(function (dataIndex) {
                if (!data.hasValue(dataIndex)) {
                    return;
                }

                var itemModel = getItemModel(data, dataIndex);
                var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt);
                var bar = createBar(data, opt, symbolMeta);
                data.setItemGraphicEl(dataIndex, bar);
                group.add(bar);
                updateCommon(bar, opt, symbolMeta);
            })
            .update(function (newIndex, oldIndex) {
                var bar = oldData.getItemGraphicEl(oldIndex);

                if (!data.hasValue(newIndex)) {
                    group.remove(bar);
                    return;
                }

                var itemModel = getItemModel(data, newIndex);
                var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt);
                var pictorialShapeStr = getShapeStr(data, symbolMeta);

                if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) {
                    group.remove(bar);
                    data.setItemGraphicEl(newIndex, null);
                    bar = null;
                }

                if (bar) {
                    updateBar(bar, opt, symbolMeta);
                } else {
                    bar = createBar(data, opt, symbolMeta, true);
                }

                data.setItemGraphicEl(newIndex, bar);
                bar.__pictorialSymbolMeta = symbolMeta; // Add back

                group.add(bar);
                updateCommon(bar, opt, symbolMeta);
            })
            .remove(function (dataIndex) {
                var bar = oldData.getItemGraphicEl(dataIndex);
                bar &&
                    removeBar(
                        oldData,
                        dataIndex,
                        bar.__pictorialSymbolMeta.animationModel,
                        bar
                    );
            })
            .execute();
        this._data = data;
        return this.group;
    },
    dispose: zrUtil.noop,
    remove: function (ecModel, api) {
        var group = this.group;
        var data = this._data;

        if (ecModel.get("animation")) {
            if (data) {
                data.eachItemGraphicEl(function (bar) {
                    removeBar(data, bar.dataIndex, ecModel, bar);
                });
            }
        } else {
            group.removeAll();
        }
    },
}); // Set or calculate default value about symbol, and calculate layout info.

function getSymbolMeta(data, dataIndex, itemModel, opt) {
    var layout = data.getItemLayout(dataIndex);
    var symbolRepeat = itemModel.get("symbolRepeat");
    var symbolClip = itemModel.get("symbolClip");
    var symbolPosition = itemModel.get("symbolPosition") || "start";
    var symbolRotate = itemModel.get("symbolRotate");
    var rotation = ((symbolRotate || 0) * Math.PI) / 180 || 0;
    var symbolPatternSize = itemModel.get("symbolPatternSize") || 2;
    var isAnimationEnabled = itemModel.isAnimationEnabled();
    var symbolMeta = {
        dataIndex: dataIndex,
        layout: layout,
        itemModel: itemModel,
        symbolType: data.getItemVisual(dataIndex, "symbol") || "circle",
        color: data.getItemVisual(dataIndex, "color"),
        symbolClip: symbolClip,
        symbolRepeat: symbolRepeat,
        symbolRepeatDirection: itemModel.get("symbolRepeatDirection"),
        symbolPatternSize: symbolPatternSize,
        rotation: rotation,
        animationModel: isAnimationEnabled ? itemModel : null,
        hoverAnimation: isAnimationEnabled && itemModel.get("hoverAnimation"),
        z2: itemModel.getShallow("z", true) || 0,
    };
    prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta);
    prepareSymbolSize(
        data,
        dataIndex,
        layout,
        symbolRepeat,
        symbolClip,
        symbolMeta.boundingLength,
        symbolMeta.pxSign,
        symbolPatternSize,
        opt,
        symbolMeta
    );
    prepareLineWidth(
        itemModel,
        symbolMeta.symbolScale,
        rotation,
        opt,
        symbolMeta
    );
    var symbolSize = symbolMeta.symbolSize;
    var symbolOffset = itemModel.get("symbolOffset");

    if (zrUtil.isArray(symbolOffset)) {
        symbolOffset = [
            parsePercent(symbolOffset[0], symbolSize[0]),
            parsePercent(symbolOffset[1], symbolSize[1]),
        ];
    }

    prepareLayoutInfo(
        itemModel,
        symbolSize,
        layout,
        symbolRepeat,
        symbolClip,
        symbolOffset,
        symbolPosition,
        symbolMeta.valueLineWidth,
        symbolMeta.boundingLength,
        symbolMeta.repeatCutLength,
        opt,
        symbolMeta
    );
    return symbolMeta;
} // bar length can be negative.

function prepareBarLength(itemModel, symbolRepeat, layout, opt, output) {
    var valueDim = opt.valueDim;
    var symbolBoundingData = itemModel.get("symbolBoundingData");
    var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis());
    var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0));
    var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0);
    var boundingLength;

    if (zrUtil.isArray(symbolBoundingData)) {
        var symbolBoundingExtent = [
            convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx,
            convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx,
        ];
        symbolBoundingExtent[1] < symbolBoundingExtent[0] &&
            symbolBoundingExtent.reverse();
        boundingLength = symbolBoundingExtent[pxSignIdx];
    } else if (symbolBoundingData != null) {
        boundingLength =
            convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx;
    } else if (symbolRepeat) {
        boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx;
    } else {
        boundingLength = layout[valueDim.wh];
    }

    output.boundingLength = boundingLength;

    if (symbolRepeat) {
        output.repeatCutLength = layout[valueDim.wh];
    }

    output.pxSign = boundingLength > 0 ? 1 : boundingLength < 0 ? -1 : 0;
}

function convertToCoordOnAxis(axis, value) {
    return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value)));
} // Support ['100%', '100%']

function prepareSymbolSize(
    data,
    dataIndex,
    layout,
    symbolRepeat,
    symbolClip,
    boundingLength,
    pxSign,
    symbolPatternSize,
    opt,
    output
) {
    var valueDim = opt.valueDim;
    var categoryDim = opt.categoryDim;
    var categorySize = Math.abs(layout[categoryDim.wh]);
    var symbolSize = data.getItemVisual(dataIndex, "symbolSize");

    if (zrUtil.isArray(symbolSize)) {
        symbolSize = symbolSize.slice();
    } else {
        if (symbolSize == null) {
            symbolSize = "100%";
        }

        symbolSize = [symbolSize, symbolSize];
    } // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is
    // to complicated to calculate real percent value if considering scaled lineWidth.
    // So the actual size will bigger than layout size if lineWidth is bigger than zero,
    // which can be tolerated in pictorial chart.

    symbolSize[categoryDim.index] = parsePercent(
        symbolSize[categoryDim.index],
        categorySize
    );
    symbolSize[valueDim.index] = parsePercent(
        symbolSize[valueDim.index],
        symbolRepeat ? categorySize : Math.abs(boundingLength)
    );
    output.symbolSize = symbolSize; // If x or y is less than zero, show reversed shape.

    var symbolScale = (output.symbolScale = [
        symbolSize[0] / symbolPatternSize,
        symbolSize[1] / symbolPatternSize,
    ]); // Follow convention, 'right' and 'top' is the normal scale.

    symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign;
}

function prepareLineWidth(itemModel, symbolScale, rotation, opt, output) {
    // In symbols are drawn with scale, so do not need to care about the case that width
    // or height are too small. But symbol use strokeNoScale, where acture lineWidth should
    // be calculated.
    var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;

    if (valueLineWidth) {
        pathForLineWidth.attr({
            scale: symbolScale.slice(),
            rotation: rotation,
        });
        pathForLineWidth.updateTransform();
        valueLineWidth /= pathForLineWidth.getLineScale();
        valueLineWidth *= symbolScale[opt.valueDim.index];
    }

    output.valueLineWidth = valueLineWidth;
}

function prepareLayoutInfo(
    itemModel,
    symbolSize,
    layout,
    symbolRepeat,
    symbolClip,
    symbolOffset,
    symbolPosition,
    valueLineWidth,
    boundingLength,
    repeatCutLength,
    opt,
    output
) {
    var categoryDim = opt.categoryDim;
    var valueDim = opt.valueDim;
    var pxSign = output.pxSign;
    var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0);
    var pathLen = unitLength; // Note: rotation will not effect the layout of symbols, because user may
    // want symbols to rotate on its center, which should not be translated
    // when rotating.

    if (symbolRepeat) {
        var absBoundingLength = Math.abs(boundingLength);
        var symbolMargin =
            zrUtil.retrieve(itemModel.get("symbolMargin"), "15%") + "";
        var hasEndGap = false;

        if (symbolMargin.lastIndexOf("!") === symbolMargin.length - 1) {
            hasEndGap = true;
            symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1);
        }

        symbolMargin = parsePercent(symbolMargin, symbolSize[valueDim.index]);
        var uLenWithMargin = Math.max(unitLength + symbolMargin * 2, 0); // When symbol margin is less than 0, margin at both ends will be subtracted
        // to ensure that all of the symbols will not be overflow the given area.

        var endFix = hasEndGap ? 0 : symbolMargin * 2; // Both final repeatTimes and final symbolMargin area calculated based on
        // boundingLength.

        var repeatSpecified = isNumeric(symbolRepeat);
        var repeatTimes = repeatSpecified
            ? symbolRepeat
            : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); // Adjust calculate margin, to ensure each symbol is displayed
        // entirely in the given layout area.

        var mDiff = absBoundingLength - repeatTimes * unitLength;
        symbolMargin = mDiff / 2 / (hasEndGap ? repeatTimes : repeatTimes - 1);
        uLenWithMargin = unitLength + symbolMargin * 2;
        endFix = hasEndGap ? 0 : symbolMargin * 2; // Update repeatTimes when not all symbol will be shown.

        if (!repeatSpecified && symbolRepeat !== "fixed") {
            repeatTimes = repeatCutLength
                ? toIntTimes(
                      (Math.abs(repeatCutLength) + endFix) / uLenWithMargin
                  )
                : 0;
        }

        pathLen = repeatTimes * uLenWithMargin - endFix;
        output.repeatTimes = repeatTimes;
        output.symbolMargin = symbolMargin;
    }

    var sizeFix = pxSign * (pathLen / 2);
    var pathPosition = (output.pathPosition = []);
    pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2;
    pathPosition[valueDim.index] =
        symbolPosition === "start"
            ? sizeFix
            : symbolPosition === "end"
            ? boundingLength - sizeFix
            : boundingLength / 2; // 'center'

    if (symbolOffset) {
        pathPosition[0] += symbolOffset[0];
        pathPosition[1] += symbolOffset[1];
    }

    var bundlePosition = (output.bundlePosition = []);
    bundlePosition[categoryDim.index] = layout[categoryDim.xy];
    bundlePosition[valueDim.index] = layout[valueDim.xy];
    var barRectShape = (output.barRectShape = zrUtil.extend({}, layout));
    barRectShape[valueDim.wh] =
        pxSign *
        Math.max(
            Math.abs(layout[valueDim.wh]),
            Math.abs(pathPosition[valueDim.index] + sizeFix)
        );
    barRectShape[categoryDim.wh] = layout[categoryDim.wh];
    var clipShape = (output.clipShape = {}); // Consider that symbol may be overflow layout rect.

    clipShape[categoryDim.xy] = -layout[categoryDim.xy];
    clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh];
    clipShape[valueDim.xy] = 0;
    clipShape[valueDim.wh] = layout[valueDim.wh];
}

function createPath(symbolMeta) {
    var symbolPatternSize = symbolMeta.symbolPatternSize;
    var path = createSymbol(
        // Consider texture img, make a big size.
        symbolMeta.symbolType,
        -symbolPatternSize / 2,
        -symbolPatternSize / 2,
        symbolPatternSize,
        symbolPatternSize,
        symbolMeta.color
    );
    path.attr({
        culling: true,
    });
    path.type !== "image" &&
        path.setStyle({
            strokeNoScale: true,
        });
    return path;
}

function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) {
    var bundle = bar.__pictorialBundle;
    var symbolSize = symbolMeta.symbolSize;
    var valueLineWidth = symbolMeta.valueLineWidth;
    var pathPosition = symbolMeta.pathPosition;
    var valueDim = opt.valueDim;
    var repeatTimes = symbolMeta.repeatTimes || 0;
    var index = 0;
    var unit =
        symbolSize[opt.valueDim.index] +
        valueLineWidth +
        symbolMeta.symbolMargin * 2;
    eachPath(bar, function (path) {
        path.__pictorialAnimationIndex = index;
        path.__pictorialRepeatTimes = repeatTimes;

        if (index < repeatTimes) {
            updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate);
        } else {
            updateAttr(
                path,
                null,
                {
                    scale: [0, 0],
                },
                symbolMeta,
                isUpdate,
                function () {
                    bundle.remove(path);
                }
            );
        }

        updateHoverAnimation(path, symbolMeta);
        index++;
    });

    for (; index < repeatTimes; index++) {
        var path = createPath(symbolMeta);
        path.__pictorialAnimationIndex = index;
        path.__pictorialRepeatTimes = repeatTimes;
        bundle.add(path);
        var target = makeTarget(index);
        updateAttr(
            path,
            {
                position: target.position,
                scale: [0, 0],
            },
            {
                scale: target.scale,
                rotation: target.rotation,
            },
            symbolMeta,
            isUpdate
        ); // FIXME
        // If all emphasis/normal through action.

        path.on("mouseover", onMouseOver).on("mouseout", onMouseOut);
        updateHoverAnimation(path, symbolMeta);
    }

    function makeTarget(index) {
        var position = pathPosition.slice(); // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index
        // Otherwise: i = index;

        var pxSign = symbolMeta.pxSign;
        var i = index;

        if (
            symbolMeta.symbolRepeatDirection === "start"
                ? pxSign > 0
                : pxSign < 0
        ) {
            i = repeatTimes - 1 - index;
        }

        position[valueDim.index] =
            unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index];
        return {
            position: position,
            scale: symbolMeta.symbolScale.slice(),
            rotation: symbolMeta.rotation,
        };
    }

    function onMouseOver() {
        eachPath(bar, function (path) {
            path.trigger("emphasis");
        });
    }

    function onMouseOut() {
        eachPath(bar, function (path) {
            path.trigger("normal");
        });
    }
}

function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) {
    var bundle = bar.__pictorialBundle;
    var mainPath = bar.__pictorialMainPath;

    if (!mainPath) {
        mainPath = bar.__pictorialMainPath = createPath(symbolMeta);
        bundle.add(mainPath);
        updateAttr(
            mainPath,
            {
                position: symbolMeta.pathPosition.slice(),
                scale: [0, 0],
                rotation: symbolMeta.rotation,
            },
            {
                scale: symbolMeta.symbolScale.slice(),
            },
            symbolMeta,
            isUpdate
        );
        mainPath.on("mouseover", onMouseOver).on("mouseout", onMouseOut);
    } else {
        updateAttr(
            mainPath,
            null,
            {
                position: symbolMeta.pathPosition.slice(),
                scale: symbolMeta.symbolScale.slice(),
                rotation: symbolMeta.rotation,
            },
            symbolMeta,
            isUpdate
        );
    }

    updateHoverAnimation(mainPath, symbolMeta);

    function onMouseOver() {
        this.trigger("emphasis");
    }

    function onMouseOut() {
        this.trigger("normal");
    }
} // bar rect is used for label.

function createOrUpdateBarRect(bar, symbolMeta, isUpdate) {
    var rectShape = zrUtil.extend({}, symbolMeta.barRectShape);
    var barRect = bar.__pictorialBarRect;

    if (!barRect) {
        barRect = bar.__pictorialBarRect = new graphic.Rect({
            z2: 2,
            shape: rectShape,
            silent: true,
            style: {
                stroke: "transparent",
                fill: "transparent",
                lineWidth: 0,
            },
        });
        bar.add(barRect);
    } else {
        updateAttr(
            barRect,
            null,
            {
                shape: rectShape,
            },
            symbolMeta,
            isUpdate
        );
    }
}

function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) {
    // If not clip, symbol will be remove and rebuilt.
    if (symbolMeta.symbolClip) {
        var clipPath = bar.__pictorialClipPath;
        var clipShape = zrUtil.extend({}, symbolMeta.clipShape);
        var valueDim = opt.valueDim;
        var animationModel = symbolMeta.animationModel;
        var dataIndex = symbolMeta.dataIndex;

        if (clipPath) {
            graphic.updateProps(
                clipPath,
                {
                    shape: clipShape,
                },
                animationModel,
                dataIndex
            );
        } else {
            clipShape[valueDim.wh] = 0;
            clipPath = new graphic.Rect({
                shape: clipShape,
            });

            bar.__pictorialBundle.setClipPath(clipPath);

            bar.__pictorialClipPath = clipPath;
            var target = {};
            target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh];
            graphic[isUpdate ? "updateProps" : "initProps"](
                clipPath,
                {
                    shape: target,
                },
                animationModel,
                dataIndex
            );
        }
    }
}

function getItemModel(data, dataIndex) {
    var itemModel = data.getItemModel(dataIndex);
    itemModel.getAnimationDelayParams = getAnimationDelayParams;
    itemModel.isAnimationEnabled = isAnimationEnabled;
    return itemModel;
}

function getAnimationDelayParams(path) {
    // The order is the same as the z-order, see `symbolRepeatDiretion`.
    return {
        index: path.__pictorialAnimationIndex,
        count: path.__pictorialRepeatTimes,
    };
}

function isAnimationEnabled() {
    // `animation` prop can be set on itemModel in pictorial bar chart.
    return (
        this.parentModel.isAnimationEnabled() && !!this.getShallow("animation")
    );
}

function updateHoverAnimation(path, symbolMeta) {
    path.off("emphasis").off("normal");
    var scale = symbolMeta.symbolScale.slice();
    symbolMeta.hoverAnimation &&
        path
            .on("emphasis", function () {
                this.animateTo(
                    {
                        scale: [scale[0] * 1.1, scale[1] * 1.1],
                    },
                    400,
                    "elasticOut"
                );
            })
            .on("normal", function () {
                this.animateTo(
                    {
                        scale: scale.slice(),
                    },
                    400,
                    "elasticOut"
                );
            });
}

function createBar(data, opt, symbolMeta, isUpdate) {
    // bar is the main element for each data.
    var bar = new graphic.Group(); // bundle is used for location and clip.

    var bundle = new graphic.Group();
    bar.add(bundle);
    bar.__pictorialBundle = bundle;
    bundle.attr("position", symbolMeta.bundlePosition.slice());

    if (symbolMeta.symbolRepeat) {
        createOrUpdateRepeatSymbols(bar, opt, symbolMeta);
    } else {
        createOrUpdateSingleSymbol(bar, opt, symbolMeta);
    }

    createOrUpdateBarRect(bar, symbolMeta, isUpdate);
    createOrUpdateClip(bar, opt, symbolMeta, isUpdate);
    bar.__pictorialShapeStr = getShapeStr(data, symbolMeta);
    bar.__pictorialSymbolMeta = symbolMeta;
    return bar;
}

function updateBar(bar, opt, symbolMeta) {
    var animationModel = symbolMeta.animationModel;
    var dataIndex = symbolMeta.dataIndex;
    var bundle = bar.__pictorialBundle;
    graphic.updateProps(
        bundle,
        {
            position: symbolMeta.bundlePosition.slice(),
        },
        animationModel,
        dataIndex
    );

    if (symbolMeta.symbolRepeat) {
        createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true);
    } else {
        createOrUpdateSingleSymbol(bar, opt, symbolMeta, true);
    }

    createOrUpdateBarRect(bar, symbolMeta, true);
    createOrUpdateClip(bar, opt, symbolMeta, true);
}

function removeBar(data, dataIndex, animationModel, bar) {
    // Not show text when animating
    var labelRect = bar.__pictorialBarRect;
    labelRect && (labelRect.style.text = null);
    var pathes = [];
    eachPath(bar, function (path) {
        pathes.push(path);
    });
    bar.__pictorialMainPath && pathes.push(bar.__pictorialMainPath); // I do not find proper remove animation for clip yet.

    bar.__pictorialClipPath && (animationModel = null);
    zrUtil.each(pathes, function (path) {
        graphic.updateProps(
            path,
            {
                scale: [0, 0],
            },
            animationModel,
            dataIndex,
            function () {
                bar.parent && bar.parent.remove(bar);
            }
        );
    });
    data.setItemGraphicEl(dataIndex, null);
}

function getShapeStr(data, symbolMeta) {
    return [
        data.getItemVisual(symbolMeta.dataIndex, "symbol") || "none",
        !!symbolMeta.symbolRepeat,
        !!symbolMeta.symbolClip,
    ].join(":");
}

function eachPath(bar, cb, context) {
    // Do not use Group#eachChild, because it do not support remove.
    zrUtil.each(bar.__pictorialBundle.children(), function (el) {
        el !== bar.__pictorialBarRect && cb.call(context, el);
    });
}

function updateAttr(
    el,
    immediateAttrs,
    animationAttrs,
    symbolMeta,
    isUpdate,
    cb
) {
    immediateAttrs && el.attr(immediateAttrs); // when symbolCip used, only clip path has init animation, otherwise it would be weird effect.

    if (symbolMeta.symbolClip && !isUpdate) {
        animationAttrs && el.attr(animationAttrs);
    } else {
        animationAttrs &&
            graphic[isUpdate ? "updateProps" : "initProps"](
                el,
                animationAttrs,
                symbolMeta.animationModel,
                symbolMeta.dataIndex,
                cb
            );
    }
}

function updateCommon(bar, opt, symbolMeta) {
    var color = symbolMeta.color;
    var dataIndex = symbolMeta.dataIndex;
    var itemModel = symbolMeta.itemModel; // Color must be excluded.
    // Because symbol provide setColor individually to set fill and stroke

    var normalStyle = itemModel.getModel("itemStyle").getItemStyle(["color"]);
    var hoverStyle = itemModel.getModel("emphasis.itemStyle").getItemStyle();
    var cursorStyle = itemModel.getShallow("cursor");
    eachPath(bar, function (path) {
        // PENDING setColor should be before setStyle!!!
        path.setColor(color);
        path.setStyle(
            zrUtil.defaults(
                {
                    fill: color,
                    opacity: symbolMeta.opacity,
                },
                normalStyle
            )
        );
        graphic.setHoverStyle(path, hoverStyle);
        cursorStyle && (path.cursor = cursorStyle);
        path.z2 = symbolMeta.z2;
    });
    var barRectHoverStyle = {};
    var barPositionOutside =
        opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)];
    var barRect = bar.__pictorialBarRect;
    setLabel(
        barRect.style,
        barRectHoverStyle,
        itemModel,
        color,
        opt.seriesModel,
        dataIndex,
        barPositionOutside
    );
    graphic.setHoverStyle(barRect, barRectHoverStyle);
}

function toIntTimes(times) {
    var roundedTimes = Math.round(times); // Escapse accurate error

    return Math.abs(times - roundedTimes) < 1e-4
        ? roundedTimes
        : Math.ceil(times);
}

var _default = BarView;
module.exports = _default;
